<!--
 * @Descripttion: 资源文件选择器
 * @version: 1.0
 * @Author: sakuya
 * @Date: 2021年10月11日16:01:40
 * @LastEditors:
 * @LastEditTime:
-->

<template>
  <div class="sc-file-select">
    <div
      v-loading="menuLoading"
      class="sc-file-select__side"
    >
      <div class="sc-file-select__side-menu">
        <el-tree
          ref="group"
          class="menu"
          :data="menu"
          :node-key="treeProps.key"
          :props="treeProps"
          :current-node-key="menu.length > 0 ? menu[0][treeProps.key] : ''"
          highlight-current
          @node-click="groupClick"
        >
          <template #default="{ node }">
            <span class="el-tree-node__label">
              <el-icon class="icon"><el-icon-folder /></el-icon>{{ node.label }}
            </span>
          </template>
        </el-tree>
      </div>
      <div
        v-if="multiple"
        class="sc-file-select__side-msg"
      >
        已选择 <b>{{ value.length }}</b> / <b>{{ max }}</b> 项
      </div>
    </div>
    <div
      v-loading="listLoading"
      class="sc-file-select__files"
    >
      <div class="sc-file-select__top">
        <div
          v-if="!hideUpload"
          class="upload"
        >
          <el-upload
            class="sc-file-select__upload"
            action=""
            multiple
            :show-file-list="false"
            :accept="accept"
            :on-change="uploadChange"
            :before-upload="uploadBefore"
            :on-progress="uploadProcess"
            :on-success="uploadSuccess"
            :on-error="uploadError"
            :http-request="uploadRequest"
          >
            <el-button
              type="primary"
              icon="el-icon-upload"
            >
              本地上传
            </el-button>
          </el-upload>
          <span class="tips"><el-icon><el-icon-warning /></el-icon>大小不超过{{
            maxSize
          }}MB</span>
        </div>
        <div class="keyword">
          <el-input
            v-model="keyword"
            prefix-icon="el-icon-search"
            placeholder="文件名搜索"
            clearable
            @keyup.enter="search"
            @clear="search"
          />
        </div>
      </div>
      <div class="sc-file-select__list">
        <el-scrollbar ref="scrollbar">
          <el-empty
            v-if="fileList.length == 0 && data.length == 0"
            description="无数据"
            :image-size="80"
          />
          <div
            v-for="(file, index) in fileList"
            :key="index"
            class="sc-file-select__item"
          >
            <div class="sc-file-select__item__file">
              <div class="sc-file-select__item__upload">
                <el-progress
                  type="circle"
                  :percentage="file.progress"
                  :width="70"
                />
              </div>
              <el-image
                :src="file.tempImg"
                fit="contain"
              />
            </div>
            <p>{{ file.name }}</p>
          </div>
          <div
            v-for="item in data"
            :key="item[fileProps.key]"
            class="sc-file-select__item"
            :class="{ active: value.includes(item[fileProps.url]) }"
            @click="select(item)"
          >
            <div class="sc-file-select__item__file">
              <div
                v-if="multiple"
                class="sc-file-select__item__checkbox"
              >
                <el-icon><el-icon-check /></el-icon>
              </div>
              <div
                v-else
                class="sc-file-select__item__select"
              >
                <el-icon><el-icon-check /></el-icon>
              </div>
              <div class="sc-file-select__item__box" />
              <el-image
                v-if="_isImg(item[fileProps.url])"
                :src="item[fileProps.url]"
                fit="contain"
                lazy
              />
              <div
                v-else
                class="item-file item-file-doc"
              >
                <i
                  v-if="files[_getExt(item[fileProps.url])]"
                  :class="files[_getExt(item[fileProps.url])].icon"
                  :style="{ color: files[_getExt(item[fileProps.url])].color }"
                />
                <i
                  v-else
                  class="sc-icon-file-list-fill"
                  style="color: #999"
                />
              </div>
            </div>
            <p :title="item[fileProps.fileName]">
              {{ item[fileProps.fileName] }}
            </p>
          </div>
        </el-scrollbar>
      </div>
      <div class="sc-file-select__pagination">
        <el-pagination
          v-model:current-page="currentPage"
          small
          background
          layout="prev, pager, next"
          :total="total"
          :page-size="pageSize"
          @current-change="reload"
        />
      </div>
      <div class="sc-file-select__do">
        <slot name="do" />
        <el-button
          type="primary"
          :disabled="value.length <= 0"
          @click="submit"
        >
          确 定
        </el-button>
      </div>
    </div>
  </div>
</template>

<script>
import config from "@/config/fileSelect";

export default {
  props: {
    modelValue: { type: Object, default: null },
    hideUpload: { type: Boolean, default: false },
    multiple: { type: Boolean, default: false },
    max: { type: Number, default: config.max },
    onlyImage: { type: Boolean, default: false },
    maxSize: { type: Number, default: config.maxSize },
  },
  data() {
    return {
      keyword: null,
      pageSize: 20,
      total: 0,
      currentPage: 1,
      data: [],
      menu: [],
      menuId: "",
      value: this.multiple ? [] : "",
      fileList: [],
      accept: this.onlyImage ? "image/gif, image/jpeg, image/png" : "",
      listLoading: false,
      menuLoading: false,
      treeProps: config.menuProps,
      fileProps: config.fileProps,
      files: config.files,
    };
  },
  watch: {
    multiple() {
      this.value = this.multiple ? [] : "";
      this.$emit("update:modelValue", JSON.parse(JSON.stringify(this.value)));
    },
  },
  mounted() {
    this.getMenu();
    this.getData();
  },
  methods: {
    //获取分类数据
    async getMenu() {
      this.menuLoading = true;
      var res = await config.menuApiObj.get();
      this.menu = res.data;
      this.menuLoading = false;
    },
    //获取列表数据
    async getData() {
      this.listLoading = true;
      var reqData = {
        [config.request.menuKey]: this.menuId,
        [config.request.page]: this.currentPage,
        [config.request.pageSize]: this.pageSize,
        [config.request.keyword]: this.keyword,
      };
      if (this.onlyImage) {
        reqData.type = "image";
      }
      var res = await config.listApiObj.get(reqData);
      var parseData = config.listParseData(res);
      this.data = parseData.rows;
      this.total = parseData.total;
      this.listLoading = false;
      this.$refs.scrollbar.setScrollTop(0);
    },
    //树点击事件
    groupClick(data) {
      this.menuId = data.id;
      this.currentPage = 1;
      this.keyword = null;
      this.getData();
    },
    //分页刷新表格
    reload() {
      this.getData();
    },
    search() {
      this.currentPage = 1;
      this.getData();
    },
    select(item) {
      const itemUrl = item[this.fileProps.url];
      if (this.multiple) {
        if (this.value.includes(itemUrl)) {
          this.value.splice(
            this.value.findIndex((f) => f == itemUrl),
            1
          );
        } else {
          this.value.push(itemUrl);
        }
      } else {
        if (this.value.includes(itemUrl)) {
          this.value = "";
        } else {
          this.value = itemUrl;
        }
      }
    },
    submit() {
      const value = JSON.parse(JSON.stringify(this.value));
      this.$emit("update:modelValue", value);
      this.$emit("submit", value);
    },
    //上传处理
    uploadChange(file, fileList) {
      file.tempImg = URL.createObjectURL(file.raw);
      this.fileList = fileList;
    },
    uploadBefore(file) {
      const maxSize = file.size / 1024 / 1024 < this.maxSize;
      if (!maxSize) {
        this.$message.warning(`上传文件大小不能超过 ${this.maxSize}MB!`);
        return false;
      }
    },
    uploadRequest(param) {
      var apiObj = config.apiObj;
      const data = new FormData();
      data.append("file", param.file);
      data.append([config.request.menuKey], this.menuId);
      apiObj
        .post(data, {
          onUploadProgress: (e) => {
            param.onProgress(e);
          },
        })
        .then((res) => {
          param.onSuccess(res);
        })
        .catch((err) => {
          param.onError(err);
        });
    },
    uploadProcess(event, file) {
      file.progress = Number(((event.loaded / event.total) * 100).toFixed(2));
    },
    uploadSuccess(res, file) {
      this.fileList.splice(
        this.fileList.findIndex((f) => f.uid == file.uid),
        1
      );
      var response = config.uploadParseData(res);
      this.data.unshift({
        [this.fileProps.key]: response.id,
        [this.fileProps.fileName]: response.fileName,
        [this.fileProps.url]: response.url,
      });
      if (!this.multiple) {
        this.value = response.url;
      }
    },
    uploadError(err) {
      this.$notify.error({
        title: "上传文件错误",
        message: err,
      });
    },
    //内置函数
    _isImg(fileUrl) {
      const imgExt = [".jpg", ".jpeg", ".png", ".gif", ".bmp"];
      const fileExt = fileUrl.substring(fileUrl.lastIndexOf("."));
      return imgExt.indexOf(fileExt) != -1;
    },
    _getExt(fileUrl) {
      return fileUrl.substring(fileUrl.lastIndexOf(".") + 1);
    },
  },
};
</script>

<style scoped>
.sc-file-select {
  display: flex;
}
.sc-file-select__files {
  flex: 1;
}

.sc-file-select__list {
  height: 400px;
}
.sc-file-select__item {
  display: inline-block;
  float: left;
  margin: 0 15px 25px 0;
  width: 110px;
  cursor: pointer;
}
.sc-file-select__item__file {
  width: 110px;
  height: 110px;
  position: relative;
}
.sc-file-select__item__file .el-image {
  width: 110px;
  height: 110px;
}
.sc-file-select__item__box {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  border: 2px solid var(--el-color-success);
  z-index: 1;
  display: none;
}
.sc-file-select__item__box::before {
  content: "";
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  background: var(--el-color-success);
  opacity: 0.2;
  display: none;
}
.sc-file-select__item:hover .sc-file-select__item__box {
  display: block;
}
.sc-file-select__item.active .sc-file-select__item__box {
  display: block;
}
.sc-file-select__item.active .sc-file-select__item__box::before {
  display: block;
}
.sc-file-select__item p {
  margin-top: 10px;
  white-space: nowrap;
  text-overflow: ellipsis;
  overflow: hidden;
  -webkit-text-overflow: ellipsis;
  text-align: center;
}
.sc-file-select__item__checkbox {
  position: absolute;
  width: 20px;
  height: 20px;
  top: 7px;
  right: 7px;
  z-index: 2;
  background: rgba(0, 0, 0, 0.2);
  border: 1px solid #fff;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
.sc-file-select__item__checkbox i {
  font-size: 14px;
  color: #fff;
  font-weight: bold;
  display: none;
}
.sc-file-select__item__select {
  position: absolute;
  width: 20px;
  height: 20px;
  top: 0px;
  right: 0px;
  z-index: 2;
  background: var(--el-color-success);
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
.sc-file-select__item__select i {
  font-size: 14px;
  color: #fff;
  font-weight: bold;
}
.sc-file-select__item.active .sc-file-select__item__checkbox {
  background: var(--el-color-success);
}
.sc-file-select__item.active .sc-file-select__item__checkbox i {
  display: block;
}
.sc-file-select__item.active .sc-file-select__item__select {
  display: flex;
}
.sc-file-select__item__file .item-file {
  width: 110px;
  height: 110px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
.sc-file-select__item__file .item-file i {
  font-size: 40px;
}
.sc-file-select__item__file .item-file.item-file-doc {
  color: #409eff;
}

.sc-file-select__item__upload {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 1;
  background: rgba(255, 255, 255, 0.7);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.sc-file-select__side {
  width: 200px;
  margin-right: 15px;
  border-right: 1px solid rgba(128, 128, 128, 0.2);
  display: flex;
  flex-flow: column;
}
.sc-file-select__side-menu {
  flex: 1;
}
.sc-file-select__side-msg {
  height: 32px;
  line-height: 32px;
}

.sc-file-select__top {
  margin-bottom: 15px;
  display: flex;
  justify-content: space-between;
}
.sc-file-select__upload {
  display: inline-block;
}
.sc-file-select__top .tips {
  font-size: 12px;
  margin-left: 10px;
  color: #999;
}
.sc-file-select__top .tips i {
  font-size: 14px;
  margin-right: 5px;
  position: relative;
  bottom: -0.125em;
}
.sc-file-select__pagination {
  margin: 15px 0;
}

.sc-file-select__do {
  text-align: right;
}
</style>
